#include "S5ActionConnect.h"
#include <KSocket.h>
#include "S5ActionClient.h"
#include "S5Def.h"
#include "S5Server.h"

void S5ActionConnect::relay(const void *data, size_t length, const KIAddress *dest)
{
    server()->send(socket(), data, length);
}

void S5ActionConnect::abortImp()
{
    // Close proxy side
    server()->ensureAlive(socket(), false);
    socket()->shutdown();
    socket()->close();
    // Close client side
    m_client->abortOnce();
    m_client = NULL;
}

void S5ActionConnect::onConnected()
{
    KIpAddress address;
    socket()->getLocalAddress(address);
    server()->ensureAlive(socket());
    m_client->postSuccess(address);
    server()->receive(socket());
}

void S5ActionConnect::onError(KDef::SockError type, DWORD error)
{
    switch (type) {
    case KDef::ErrorAddToIocp:
        m_client->postFailure(S5_REP_GENERAL_SERVER_FAILURE);
        break;
    case KDef::ErrorCallConnect:
    case KDef::ErrorConnectorInit:
        {
            switch (error) {
            case WSAECONNREFUSED:
                m_client->postFailure(S5_REP_CONNECTION_REFUSED);
                break;
            case WSAEHOSTUNREACH:
                m_client->postFailure(S5_REP_HOST_UNREACHABLE);
                break;
            case WSAENETUNREACH:
                m_client->postFailure(S5_REP_NETWORK_UNREACHABLE);
                break;
            case WSAETIMEDOUT:
                m_client->postFailure(S5_REP_TTL_EXPIRED);
                break;
            default:
                m_client->postFailure(S5_REP_GENERAL_SERVER_FAILURE);
                break;
            }
        } break;
    case KDef::ErrorFailedConnect:
        m_client->postFailure(S5_REP_CONNECTION_REFUSED);
        break;
    default:
        abortOnce();
        break;
    }
}

void S5ActionConnect::onReceived(KKey *&key, void *data, size_t length, bool more)
{
    S5Action::onReceived(key, data, length, more);
    if (S5ActionClient *client = m_client) {
        client->relay(data, length, NULL);
    }
}

void S5ActionConnect::onSent(KKey *&key, void *data, size_t length)
{
    S5Action::onSent(key, data, length);
    if (S5ActionClient *client = m_client) {
        client->onPeerRelayed();
    }
}